package org.misty.seckill.interceptor;

import org.misty.seckill.exception.AuthorizationException;
import org.misty.seckill.redis.SessionKey;
import org.misty.seckill.service.LoginService;
import org.misty.seckill.util.ServletUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class AuthorizationInterceptor2 implements HandlerInterceptor {
  private final LoginService loginService;
  private final boolean defaultRequire;
  private final ConcurrentHashMap<String, Boolean> cache = new ConcurrentHashMap<>();

  public AuthorizationInterceptor2(
      @Autowired LoginService loginService,
      @Autowired @Qualifier("isAuthorizationEnable") boolean enable
  ) {
    this.loginService = loginService;
    this.defaultRequire = enable;
  }

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    var methodSign = request.getRequestURI();
    if ("/error".equals(methodSign)) { // 不拦截error
      return true;
    }
    var requireAuth = defaultRequire;
    if (handler instanceof HandlerMethod) {
      methodSign = ((HandlerMethod) handler).getMethod().toString();
    }
    if (cache.containsKey(methodSign)) {
      requireAuth = cache.get(methodSign);
    } else {
      if (handler instanceof HandlerMethod) {
        var mh = (HandlerMethod) handler;
        var beanType = mh.getBeanType();
        if (beanType.isAnnotationPresent(RequireAuth.class)) {
          requireAuth = true;
        } else if (beanType.isAnnotationPresent(IgnoreAuth.class)) {
          requireAuth = false;
        }
        if (mh.hasMethodAnnotation(RequireAuth.class)) {
          requireAuth = true;
        } else if (mh.hasMethodAnnotation(IgnoreAuth.class)) {
          requireAuth = false;
        }
      }
      cache.put(methodSign, requireAuth);
    }

    var token = ServletUtil.readSessionToken(request);
    var user = loginService.getByToken(token);
    if (user != null) {
      request.setAttribute("token", token);
      request.setAttribute("user", user);
      loginService.keepAlive(token, user); // 刷新redis
      ServletUtil.writeSessionToken(response, token, SessionKey.TOKEN.getExpiryInSec(), "/");
    }

    if (requireAuth && user == null) { // 需要登录但未登录
      if ("GET".equalsIgnoreCase(request.getMethod()) && ServletUtil.isHtmlRequest(request) && !ServletUtil.isAjaxRequest(request)) {
        // 缓存当前访问的路径，并跳转到登录页面（由异常处理器负责）
        token = loginService.cacheRedirect(token, request.getRequestURI());
        ServletUtil.writeSessionToken(response, token, SessionKey.REDIRECT.getExpiryInSec(), "/");
      }
      throw AuthorizationException.signOut();
    }

    return true;
  }

  @Retention(RetentionPolicy.RUNTIME)
  @Target({ElementType.METHOD, ElementType.TYPE})
  public @interface RequireAuth {
  }

  @Retention(RetentionPolicy.RUNTIME)
  @Target({ElementType.METHOD, ElementType.TYPE})
  public @interface IgnoreAuth {
  }
}
